package com.googlecode.uipipedream.impl;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.util.Observer;

import com.googlecode.uipipedream.spec.ColourModel;
import com.googlecode.uipipedream.spec.HSBType;
import com.googlecode.uipipedream.spec.SliderBar;

/**
 * <p>
 * Implementation of the H, S and B slider bars
 * </p>
 * 
 * <p>
 * This file is part of UIPipeDream.
 * </p>
 * 
 * <p>
 * You can redistribute UIPipeDream and/or modify it under the terms of the GNU
 * General Public License as published by the Free Software Foundation, either
 * version 3 of the License, or (at your option) any later version.
 * </p>
 * 
 * <p>
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * </p>
 * 
 * <p>
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <a
 * href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>.
 * </p>
 * 
 * @author <a href="http://daemonsadvocate.blogspot.com/">Matthew B. Gray</a>
 * @author James Jones
 * @version 1.0 Final
 */
public class VerticalSliderBar extends SliderBar implements Observer {
	private static final long serialVersionUID = -5668151225831548319L;
	private Rectangle boundingBox;
	private ColourModel model;
	private boolean draggingBox = false;
	private HSBType type;
	private Dimension cachedCanvasSize;
	private Image drawing;
	private Graphics graphics = null;
	
	public VerticalSliderBar(ColourModel model, HSBType type) {
		super(model);
		this.model = model;
		this.type = type;
	}

	@Override public Dimension preferredSize() { return new Dimension(30, 80); }
	@Override public Dimension minimumSize() { return new Dimension(30, 80); }
	
	@Override public synchronized void paint(Graphics g) {
		// 0. Cache dimensions and setup object.
		Dimension d = getSize();
		if ((this.graphics == null)
				|| (d.width != cachedCanvasSize.width)
				|| (d.height != cachedCanvasSize.height)) {
			this.cachedCanvasSize = d;
			this.drawing = createImage(d.width, d.height);
			this.graphics = drawing.getGraphics();
			//0.96 * d.height
			// draw the bounding box 2% in from all edges.
			final int x = (int) (0.02 * d.width);
			final int y = (int) (0.02 * d.height);
			final int width = (int)(0.96 * d.width);
			final int height = (int)(0.96 * d.height);
			
			this.boundingBox = new Rectangle(x,y,width,height);
		}

		// 1. Erase the previous image.
		this.graphics.setColor(getBackground());
		this.graphics.fillRect(0, 0, d.width, d.height);
		this.graphics.setColor(Color.black);

		// 2. Paint the brightness bar
		for (int i = 0; i < boundingBox.height; ++i) {
			float offset = 1f - (float) i / boundingBox.height;
			switch (type) {
			case HUE:
				this.graphics.setColor(Color.getHSBColor(offset,model.getSaturation(), model.getBrightness()));
				break;
			case SATURATION:
				this.graphics.setColor(Color.getHSBColor(model.getHue(), offset, model.getBrightness()));
				break;
			case BRIGHTNESS:
				this.graphics.setColor(Color.getHSBColor(model.getHue(), model.getSaturation(), offset));
				break;
			}
			this.graphics.drawLine(
					boundingBox.x, boundingBox.y + i,                            // Point 1
					boundingBox.x + boundingBox.width - 1, boundingBox.y + i);   // Point 2
		}
		this.graphics.setColor(Color.white);
		this.graphics.drawRect(boundingBox.x, boundingBox.y, boundingBox.width - 1, boundingBox.height - 1);

		// 3. Paint brightness cursor
		float f = 0;
		switch (type) {
		case HUE:
			f = model.getHue();
			break;
		case SATURATION:
			f = model.getSaturation();
			break;
		case BRIGHTNESS:
			f = model.getBrightness();
			break;
		}
		int yy = boundingBox.y + (int) (boundingBox.height * (1.0 - f));
		this.graphics.drawLine(boundingBox.x, yy, boundingBox.x + boundingBox.width - 1, yy);
		this.graphics.drawLine(boundingBox.x - 2, yy, boundingBox.x - 7, yy + 3);
		this.graphics.drawLine(boundingBox.x - 2, yy, boundingBox.x - 7, yy - 3);
		this.graphics.drawLine(boundingBox.x - 7, yy - 3, boundingBox.x - 7, yy + 3);

		g.drawImage(drawing, 0, 0, this);
	}

	@Override public boolean mouseDrag(Event e, int x, int y) {
		if (draggingBox) {
			float f = 1f - (float) (y - boundingBox.y) / boundingBox.height;
			if (f < 0) f = 0f;
			if (f > 1f) f = 1f;
			switch (type) {
			case HUE:
				model.setHue(f);
				break;
			case SATURATION:
				model.setSaturation(f);
				break;
			case BRIGHTNESS:
				model.setBrightness(f);
				break;
			}
			return true;
		}
		return false;
	}
	
	@Override public boolean mouseDown(Event e, int x, int y) {
		draggingBox = false;

		if (boundingBox.contains(x, y)) {
			draggingBox = true;
			float f = 1f - (float) (y - boundingBox.y) / boundingBox.height;
			switch (type) {
			case HUE:
				model.setHue(f);
				break;
			case SATURATION:
				model.setSaturation(f);
				break;
			case BRIGHTNESS:
				model.setBrightness(f);
				break;
			}
			return true;
		}
		float f = 0;
		switch (type) {
		case HUE:
			f = model.getHue();
			break;
		case SATURATION:
			f = model.getSaturation();
			break;
		case BRIGHTNESS:
			f = model.getBrightness();
			break;
		}
		int dy = y - boundingBox.y - (int) (boundingBox.height * (1f - f));
		if (x < boundingBox.x && x >= boundingBox.x - 8 && dy >= -4 && dy <= 4) {
			draggingBox = true;
			return true;
		}
		return false;
	}
}